// -*- mode:c++ -*-

// Copyright (c) 2020 ARM Limited
// Copyright (c) 2020 Metempsy Technology Consulting
// All rights reserved
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder.  You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

let {{

    header_output = ""
    decoder_output = ""
    exec_output = ""

    def pacEnabledCode(hint):
        if hint:
            code = """
                if (!HaveExt(xc->tcBase(), ArmExtension::FEAT_PAuth)) {
                    return NoFault;
                }
                """
        else:
            code = """
                if (!HaveExt(xc->tcBase(), ArmExtension::FEAT_PAuth)) {
                    return std::make_shared<UndefinedInstruction>(
                        machInst, true);
                }
                """
        return code

    def buildPauthObject(mnem, templateBase, opcode, hint, optArgs=[]):
        global header_output, decoder_output, exec_output
        pac_code = '''
            %(enabled)s

            uint64_t res;
            fault = %(op)s(xc->tcBase(), %(op1)s, %(op2)s, &res);
            XDest = res;
            '''
        if templateBase=='DataX2Reg':
            code = pac_code % {"enabled": pacEnabledCode(hint),
                               "op1": 'Op164',
                               "op2": 'Op264',
                               "op":  opcode }
        else:
            code = pac_code % {"enabled": pacEnabledCode(hint),
                               "op1": 'XDest',
                               "op2": 'Op164',
                               "op":  opcode }

        iop = ArmInstObjParams(mnem, mnem.capitalize(),
                               templateBase+"Op", code, optArgs)
        header_output += eval(templateBase + "Declare").subst(iop)
        decoder_output += eval(templateBase + "Constructor").subst(iop)
        exec_output += BasicExecute.subst(iop)

    def buildXPauthObject(mnem, hint, optArgs=[]):
        global header_output, decoder_output, exec_output
        templateBase = "XPauthOpRegReg"

        code = pacEnabledCode(hint) + """
            uint64_t res;
            stripPAC(xc->tcBase(), XDest, data, &res);
            XDest = res;
            """
        regoptype = 'RegOp'

        iop = ArmInstObjParams(mnem, mnem.capitalize(),
                               regoptype, code, optArgs)
        header_output += eval(templateBase + "Declare").subst(iop)
        decoder_output += eval(templateBase + "Constructor").subst(iop)
        exec_output += BasicExecute.subst(iop)


    buildPauthObject("pacda",  "DataX1Reg", 'addPACDA', hint=False)
    buildPauthObject("pacdza", "DataX1Reg", 'addPACDA', hint=False)
    buildPauthObject("pacdb",  "DataX1Reg", 'addPACDB', hint=False)
    buildPauthObject("pacdzb", "DataX1Reg", 'addPACDB', hint=False)
    buildPauthObject("pacga",  "DataX2Reg", 'addPACGA', hint=False)

    buildPauthObject("pacia",     "DataX1Reg", 'addPACIA', hint=False)
    buildPauthObject("pacia1716", "DataX1Reg", 'addPACIA', hint=True)
    buildPauthObject("paciasp",   "DataX1Reg", 'addPACIA', hint=True)
    buildPauthObject("paciaz",    "DataX1Reg", 'addPACIA', hint=True)
    buildPauthObject("paciza",    "DataX1Reg", 'addPACIA', hint=False)

    buildPauthObject("pacib",     "DataX1Reg", 'addPACIB', hint=False)
    buildPauthObject("pacib1716", "DataX1Reg", 'addPACIB', hint=True)
    buildPauthObject("pacibsp",   "DataX1Reg", 'addPACIB', hint=True)
    buildPauthObject("pacibz",    "DataX1Reg", 'addPACIB', hint=True)
    buildPauthObject("pacizb",    "DataX1Reg", 'addPACIB', hint=False)

    buildPauthObject("autda",     "DataX1Reg", 'authDA', hint=False)
    buildPauthObject("autdza",    "DataX1Reg", 'authDA', hint=False)
    buildPauthObject("autdb",     "DataX1Reg", 'authDB', hint=False)
    buildPauthObject("autdzb",    "DataX1Reg", 'authDB', hint=False)

    buildPauthObject("autia",     "DataX1Reg", 'authIA', hint=False)
    buildPauthObject("autia1716", "DataX1Reg", 'authIA', hint=True)
    buildPauthObject("autiasp",   "DataX1Reg", 'authIA', hint=True)
    buildPauthObject("autiaz",    "DataX1Reg", 'authIA', hint=True)
    buildPauthObject("autiza",    "DataX1Reg", 'authIA', hint=False)

    buildPauthObject("autib",     "DataX1Reg", 'authIB', hint=False)
    buildPauthObject("autib1716", "DataX1Reg", 'authIB', hint=True)
    buildPauthObject("autibsp",   "DataX1Reg", 'authIB', hint=True)
    buildPauthObject("autibz",    "DataX1Reg", 'authIB', hint=True)
    buildPauthObject("autizb",    "DataX1Reg", 'authIB', hint=False)

    buildXPauthObject("xpacd", hint=False)
    buildXPauthObject("xpaci", hint=False)
    buildXPauthObject("xpaclri", hint=True)
}};
